// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package walk

import (
	"fmt"
	"go/constant"
	"go/token"
	"strings"

	"cmd/compile/internal/base"
	"cmd/compile/internal/escape"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/reflectdata"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
)

// 重写append（src，x，y，z），以便在append之前在
// x，y，z中的任何副作用（包括运行时恐慌）。
// 初始化语句中评估
// 对于正常的代码生成，请到此为止，将
// 剩余部分留给cgen_append。
// 
// 用于种族检测器，扩展append（src，a[，b]*）到
// 
// init{
// s:=src 
// const argc=len（args）-1 
// if cap（s）-len
// s[n+1]=b 
// 。。。
// /}
// s 
func walkAppend(n *ir.CallExpr, init *ir.Nodes, dst ir.Node) ir.Node {
	if !ir.SameSafeExpr(dst, n.Args[0]) {
		n.Args[0] = safeExpr(n.Args[0], init)
		n.Args[0] = walkExpr(n.Args[0], init)
	}
	walkExprListSafe(n.Args[1:], init)

	nsrc := n.Args[0]

	// walkExprListSafe将不使用OINDEX（s[n]），如果s 
	// 和n都是名称或文字，但它们可能会索引我们在这里修改的片段。明确地修复。
	// 使用cheapExpr还可以确保在我们开始以可见的方式修改切片之前，所有参数（尤其是任何恐慌）的评估都会发生。
	ls := n.Args[1:]
	for i, n := range ls {
		n = cheapExpr(n, init)
		if !types.Identical(n.Type(), nsrc.Type().Elem()) {
			n = typecheck.AssignConv(n, nsrc.Type().Elem(), "append")
			n = walkExpr(n, init)
		}
		ls[i] = n
	}

	argc := len(n.Args) - 1
	if argc < 1 {
		return nsrc
	}

	// 一般情况下，没有函数调用作为参数。
	// 请转到gen，除非检测需要旧的形式。
	if !base.Flag.Cfg.Instrumenting || base.Flag.CompilingRuntime {
		return n
	}

	var l []ir.Node

	ns := typecheck.Temp(nsrc.Type())
	l = append(l, ir.NewAssignStmt(base.Pos, ns, nsrc)) // s=src 

	na := ir.NewInt(int64(argc))                 // const argc 
	nif := ir.NewIfStmt(base.Pos, nil, nil, nil) // if cap（s）-len（s）<argc 
	nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, ir.NewBinaryExpr(base.Pos, ir.OSUB, ir.NewUnaryExpr(base.Pos, ir.OCAP, ns), ir.NewUnaryExpr(base.Pos, ir.OLEN, ns)), na)

	fn := typecheck.LookupRuntime("growslice") // /growtslice（<type>，old[]T，mincap int）（ret[]T）
	fn = typecheck.SubstArgTypes(fn, ns.Type().Elem(), ns.Type().Elem())

	nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, ns, mkcall1(fn, ns.Type(), nif.PtrInit(), reflectdata.TypePtr(ns.Type().Elem()), ns,
		ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, ns), na)))}

	l = append(l, nif)

	nn := typecheck.Temp(types.Types[types.TINT])
	l = append(l, ir.NewAssignStmt(base.Pos, nn, ir.NewUnaryExpr(base.Pos, ir.OLEN, ns))) // n=len。。。s[：n+argc]

	slice.SetBounded(true)
	l = append(l, ir.NewAssignStmt(base.Pos, ns, slice)) // s=s[：n+argc]

	ls = n.Args[1:]
	for i, n := range ls {
		ix := ir.NewIndexExpr(base.Pos, ns, nn) // s[n]。。。
		ix.SetBounded(true)
		l = append(l, ir.NewAssignStmt(base.Pos, ix, n)) // s[n]=arg 
		if i+1 < len(ls) {
			l = append(l, ir.NewAssignStmt(base.Pos, nn, ir.NewBinaryExpr(base.Pos, ir.OADD, nn, ir.NewInt(1)))) // /n=n+1 
		}
	}

	typecheck.Stmts(l)
	walkStmtList(l)
	init.Append(l...)
	return ns
}

// walkClose在一个OCLOSE节点上行走。
func walkClose(n *ir.UnaryExpr, init *ir.Nodes) ir.Node {
	// 无法使用chanfn-closechan接受任何，而不是chan任何
	fn := typecheck.LookupRuntime("closechan")
	fn = typecheck.SubstArgTypes(fn, n.X.Type())
	return mkcall1(fn, nil, init, n.X)
}

// 到memmove调用或运行时调用的较低副本（a，b）。
// 
// init{
// n:=len（a）
// if n>len（b）{n=len（b）}
// if a.ptr！=b、 ptr{memmove（a.ptr，b.ptr，n*sizeof（elem（a））}
// }
// n；
// 
// 如果b是字符串，也可以使用。
// 
func walkCopy(n *ir.BinaryExpr, init *ir.Nodes, runtimecall bool) ir.Node {
	if n.X.Type().Elem().HasPointers() {
		ir.CurFunc.SetWBPos(n.Pos())
		fn := writebarrierfn("typedslicecopy", n.X.Type().Elem(), n.Y.Type().Elem())
		n.X = cheapExpr(n.X, init)
		ptrL, lenL := backingArrayPtrLen(n.X)
		n.Y = cheapExpr(n.Y, init)
		ptrR, lenR := backingArrayPtrLen(n.Y)
		return mkcall1(fn, n.Type(), init, reflectdata.TypePtr(n.X.Type().Elem()), ptrL, lenL, ptrR, lenR)
	}

	if runtimecall {
		// 依赖于运行时插入指令：
		// 复制（n.左，n.右）
		// n.右可以是片或字符串。如果n>len（frm）{n=len（frm）}。ptr！=frm公司。ptr{memmove（…）}

		n.X = cheapExpr(n.X, init)
		ptrL, lenL := backingArrayPtrLen(n.X)
		n.Y = cheapExpr(n.Y, init)
		ptrR, lenR := backingArrayPtrLen(n.Y)

		fn := typecheck.LookupRuntime("slicecopy")
		fn = typecheck.SubstArgTypes(fn, ptrL.Type().Elem(), ptrR.Type().Elem())

		return mkcall1(fn, n.Type(), init, ptrL, lenL, ptrR, lenR, ir.NewInt(n.X.Type().Elem().Size()))
	}

	n.X = walkExpr(n.X, init)
	n.Y = walkExpr(n.Y, init)
	nl := typecheck.Temp(n.X.Type())
	nr := typecheck.Temp(n.Y.Type())
	var l []ir.Node
	l = append(l, ir.NewAssignStmt(base.Pos, nl, n.X))
	l = append(l, ir.NewAssignStmt(base.Pos, nr, n.Y))

	nfrm := ir.NewUnaryExpr(base.Pos, ir.OSPTR, nr)
	nto := ir.NewUnaryExpr(base.Pos, ir.OSPTR, nl)

	nlen := typecheck.Temp(types.Types[types.TINT])

	l = append(l, ir.NewAssignStmt(base.Pos, nlen, ir.NewUnaryExpr(base.Pos, ir.OLEN, nl)))

	nif := ir.NewIfStmt(base.Pos, nil, nil, nil)

	nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OGT, nlen, ir.NewUnaryExpr(base.Pos, ir.OLEN, nr))
	nif.Body.Append(ir.NewAssignStmt(base.Pos, nlen, ir.NewUnaryExpr(base.Pos, ir.OLEN, nr)))
	l = append(l, nif)

	ne := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.ONE, nto, nfrm), nil, nil)
	ne.Likely = true
	l = append(l, ne)

	fn := typecheck.LookupRuntime("memmove")
	fn = typecheck.SubstArgTypes(fn, nl.Type().Elem(), nl.Type().Elem())
	nwid := ir.Node(typecheck.Temp(types.Types[types.TUINTPTR]))
	setwid := ir.NewAssignStmt(base.Pos, nwid, typecheck.Conv(nlen, types.Types[types.TUINTPTR]))
	ne.Body.Append(setwid)
	nwid = ir.NewBinaryExpr(base.Pos, ir.OMUL, nwid, ir.NewInt(nl.Type().Elem().Size()))
	call := mkcall1(fn, nil, init, nto, nfrm, nwid)
	ne.Body.Append(call)

	typecheck.Stmts(l)
	walkStmtList(l)
	init.Append(l...)
	return nlen
}

// walkDelete遍历一个ODELETE节点。
func walkDelete(init *ir.Nodes, n *ir.CallExpr) ir.Node {
	init.Append(ir.TakeInit(n)...)
	map_ := n.Args[0]
	key := n.Args[1]
	map_ = walkExpr(map_, init)
	key = walkExpr(key, init)

	t := map_.Type()
	fast := mapfast(t)
	key = mapKeyArg(fast, n, key)
	return mkcall1(mapfndel(mapdelete[fast], t), nil, init, reflectdata.TypePtr(t), map_, key)
}

// walkLenCap遍历OLEN或OCAP节点。
func walkLenCap(n *ir.UnaryExpr, init *ir.Nodes) ir.Node {
	if isRuneCount(n) {
		// 将len（[]符文（字符串））替换为runtime。countrunes（字符串）。
		return mkcall("countrunes", n.Type(), init, typecheck.Conv(n.X.(*ir.ConvExpr).X, types.Types[types.TSTRING]))
	}

	n.X = walkExpr(n.X, init)

	// 将len（*[10]int）替换为10。
	// 推迟到现在以保留副作用。
	t := n.X.Type()

	if t.IsPtr() {
		t = t.Elem()
	}
	if t.IsArray() {
		safeExpr(n.X, init)
		con := typecheck.OrigInt(n, t.NumElem())
		con.SetTypecheck(1)
		return con
	}
	return n
}

// walkMakeChan在OMAKECHAN节点上行走。
func walkMakeChan(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
	// 当大小适合int时，使用makechan而不是
	// makechan64，它在32位平台上更快、更短。
	size := n.Len
	fnname := "makechan64"
	argtype := types.Types[types.TINT64]

	// 类型检查保证TIDEAL大小为正并适合整数。
	// 将TUINT或TUINTPTR转换为TINT时大小溢出的情况
	// 将在运行时由makechan中的负范围检查处理。
	if size.Type().IsKind(types.TIDEAL) || size.Type().Size() <= types.Types[types.TUINT].Size() {
		fnname = "makechan"
		argtype = types.Types[types.TINT]
	}

	return mkcall1(chanfn(fnname, 1, n.Type()), n.Type(), init, reflectdata.TypePtr(n.Type()), typecheck.Conv(size, argtype))
}

// walkMakeMap遍历OMAKEMAP节点。
func walkMakeMap(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
	t := n.Type()
	hmapType := reflectdata.MapType(t)
	hint := n.Len

	// var h*hmap 
	var h ir.Node
	if n.Esc() == ir.EscNone {
		// 在堆栈上分配hmap。

		// var-hv-hmap-
		// h=&hv-
		h = stackTempAddr(init, hmapType)

		// 分配一个hmap指向的桶。如果提示
		// 不大于BUCKETSIZE，则堆栈上的桶数。如果提示大于
		// BUCKETSIZE运行时。makemap将在堆上分配桶。
		// 最大密钥和元素大小为128字节，较大的对象
		// 以间接方式存储。所以最大桶大小是2048+每股收益。
		if !ir.IsConst(hint, constant.Int) ||
			constant.Compare(hint.Val(), token.LEQ, constant.MakeInt64(reflectdata.BUCKETSIZE)) {

			// 以防提示大于BUCKETSIZE运行时。makemap 
			// 将在堆上分配桶，参见#20184 
			// 
			// if hint<=BUCKETSIZE{
			// var bv bmap 
			// b=&bv 
			// h.buckets=b 
			// 

			nif := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OLE, hint, ir.NewInt(reflectdata.BUCKETSIZE)), nil, nil)
			nif.Likely = true

			// var bv bv bmap 
			// b=&bv 
			b := stackTempAddr(&nif.Body, reflectdata.MapBucketType(t))

			bsym := hmapType.Field(5).Sym // /hmap。水桶见倒影。go:hmap 
			na := ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, h, bsym), b)
			nif.Body.Append(na)
			appendWalkStmt(init, nif)
		}
	}

	if ir.IsConst(hint, constant.Int) && constant.Compare(hint.Val(), token.LEQ, constant.MakeInt64(reflectdata.BUCKETSIZE)) {
		// 处理make（map[any]any）和
		// make（map[any]any，hint），其中hint<=BUCKETSIZE 
		// 特别允许更快的映射初始化和
		// 通过使用参数较少的调用来改进二进制大小。
		// For hint<=BUCKETSIZE重载因子（hint，0）为假
		// makemap不会分配任何存储桶。因此，
		// 此代码路径中不需要分配存储桶。
		if n.Esc() == ir.EscNone {
			// 只需要初始化h.hash0，因为
			// hmap h已经在堆栈上分配了。
			// h.hash0=fastrand（）
			rand := mkcall("fastrand", types.Types[types.TUINT32], init)
			hashsym := hmapType.Field(4).Sym // hmap。hash0见reflect。go:hmap 
			appendWalkStmt(init, ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, h, hashsym), rand))
			return typecheck.ConvNop(h, t)
		}
		// 调用运行时。makehmap在堆上分配一个
		// hmap，并初始化hmap的hash0字段。
		fn := typecheck.LookupRuntime("makemap_small")
		fn = typecheck.SubstArgTypes(fn, t.Key(), t.Elem())
		return mkcall1(fn, n.Type(), init)
	}

	if n.Esc() != ir.EscNone {
		h = typecheck.NodNil()
	}
	// 带有变量或大提示的映射初始化更复杂。因此，我们生成对
	// 运行时的调用。makemap初始化hmap并分配
	// 映射桶。

	// 当提示适合int时，使用makemap而不是
	// makemap64，这在32位平台上更快、更短。
	fnname := "makemap64"
	argtype := types.Types[types.TINT64]

	// 类型检查保证TIDEAL提示为正并适合整数。
	// 请参见typecheck1函数中OpSwitch中OMAKE case的TMAP case中的checkmake call。
	// 将TUINT或TUINTPTR转换为TINT时出现提示溢出的情况
	// 将在运行时通过makemap中的负范围检查来处理。
	if hint.Type().IsKind(types.TIDEAL) || hint.Type().Size() <= types.Types[types.TUINT].Size() {
		fnname = "makemap"
		argtype = types.Types[types.TINT]
	}

	fn := typecheck.LookupRuntime(fnname)
	fn = typecheck.SubstArgTypes(fn, hmapType, t.Key(), t.Elem())
	return mkcall1(fn, n.Type(), init, reflectdata.TypePtr(n.Type()), typecheck.Conv(hint, argtype), h)
}

// walkMakeSlice遍历OMAKESLICE节点。
func walkMakeSlice(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
	l := n.Len
	r := n.Cap
	if r == nil {
		r = safeExpr(l, init)
		l = r
	}
	t := n.Type()
	if t.Elem().NotInHeap() {
		base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem())
	}
	if n.Esc() == ir.EscNone {
		if why := escape.HeapAllocReason(n); why != "" {
			base.Fatalf("%v has EscNone, but %v", n, why)
		}
		// var arr[r]T 
		// n=arr[：l]
		i := typecheck.IndexConst(r)
		if i < 0 {
			base.Fatalf("walkExpr: invalid index %v", r)
		}

		// cap被限制为[0,2^31]或[0,2^63]，这取决于
		// 我们是在32位还是64位系统中。因此，这样做是安全的：
		// 
		// if uint64（len）>cap 
		// panicmakeslicecap（）
		// }。
		nif := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OGT, typecheck.Conv(l, types.Types[types.TUINT64]), ir.NewInt(i)), nil, nil)
		niflen := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OLT, l, ir.NewInt(0)), nil, nil)
		niflen.Body = []ir.Node{mkcall("panicmakeslicelen", nil, init)}
		nif.Body.Append(niflen, mkcall("panicmakeslicecap", nil, init))
		init.Append(typecheck.Stmt(nif))

		var_ := typecheck.Temp(t)
		return walkExpr(typecheck.Expr(typecheck.Conv(r, n.Type())), init)
	}

	// n逃逸；设置对makeslice的调用。
	// 当len和cap可以放入int时，使用makeslice代替
	// makeslice64，这在32位平台上更快、更短。

	len, cap := l, r

	fnname := "makeslice64"
	argtype := types.Types[types.TINT64]

	// 类型检查保证TIDEAL len/cap为正数并适合int。
	// 将TUINT或TUINTPTR转换为TINT时出现len或cap溢出的情况
	// 将在运行时由makeslice中的负范围检查处理。
	if (len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size()) &&
		(cap.Type().IsKind(types.TIDEAL) || cap.Type().Size() <= types.Types[types.TUINT].Size()) {
		fnname = "makeslice"
		argtype = types.Types[types.TINT]
	}
	fn := typecheck.LookupRuntime(fnname)
	ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.TypePtr(t.Elem()), typecheck.Conv(len, argtype), typecheck.Conv(cap, argtype))
	ptr.MarkNonNil()
	len = typecheck.Conv(len, types.Types[types.TINT])
	cap = typecheck.Conv(cap, types.Types[types.TINT])
	sh := ir.NewSliceHeaderExpr(base.Pos, t, ptr, len, cap)
	return walkExpr(typecheck.Expr(sh), init)
}

// WalkMakeSiceCopy遍历OMAKESLICECOPY节点。
func walkMakeSliceCopy(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
	if n.Esc() == ir.EscNone {
		base.Fatalf("OMAKESLICECOPY with EscNone: %v", n)
	}

	t := n.Type()
	if t.Elem().NotInHeap() {
		base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem())
	}

	length := typecheck.Conv(n.Len, types.Types[types.TINT])
	copylen := ir.NewUnaryExpr(base.Pos, ir.OLEN, n.Cap)
	copyptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, n.Cap)

	if !t.Elem().HasPointers() && n.Bounded() {
		// 当len（to）==len（from）且元素没有指针时：
		// 用运行时替换make+copy。mallocgc+运行时。memmove。

		// 我们不检查len（to）*元素是否溢出。这里的宽度
		// 因为len（from）是一个现有的检查片容量
		// 具有相同的元素。from切片的宽度。
		size := ir.NewBinaryExpr(base.Pos, ir.OMUL, typecheck.Conv(length, types.Types[types.TUINTPTR]), typecheck.Conv(ir.NewInt(t.Elem().Size()), types.Types[types.TUINTPTR]))

		// 实例化mallocgc（大小为uintpttr，类型为*字节，needszero bool）不安全。指针
		fn := typecheck.LookupRuntime("mallocgc")
		ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, size, typecheck.NodNil(), ir.NewBool(false))
		ptr.MarkNonNil()
		sh := ir.NewSliceHeaderExpr(base.Pos, t, ptr, length, length)

		s := typecheck.Temp(t)
		r := typecheck.Stmt(ir.NewAssignStmt(base.Pos, s, sh))
		r = walkExpr(r, init)
		init.Append(r)

		// 实例化memmove（到*any，frm*any，size uintptpr）
		fn = typecheck.LookupRuntime("memmove")
		fn = typecheck.SubstArgTypes(fn, t.Elem(), t.Elem())
		ncopy := mkcall1(fn, nil, init, ir.NewUnaryExpr(base.Pos, ir.OSPTR, s), copyptr, size)
		init.Append(walkExpr(typecheck.Stmt(ncopy), init))

		return s
	}
	// 用运行时替换make+copy。复制。
	// 实例化makeslicecopy（typ*byte，tolen int，fromlen int，from unsafe.Pointer）不安全。指针
	fn := typecheck.LookupRuntime("makeslicecopy")
	ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.TypePtr(t.Elem()), length, copylen, typecheck.Conv(copyptr, types.Types[types.TUNSAFEPTR]))
	ptr.MarkNonNil()
	sh := ir.NewSliceHeaderExpr(base.Pos, t, ptr, length, length)
	return walkExpr(typecheck.Expr(sh), init)
}

// walkNew遍历一个新节点。
func walkNew(n *ir.UnaryExpr, init *ir.Nodes) ir.Node {
	t := n.Type().Elem()
	if t.NotInHeap() {
		base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", n.Type().Elem())
	}
	if n.Esc() == ir.EscNone {
		if t.Size() > ir.MaxImplicitStackVarSize {
			base.Fatalf("large ONEW with EscNone: %v", n)
		}
		return stackTempAddr(init, t)
	}
	types.CalcSize(t)
	n.MarkNonNil()
	return n
}

// 生成用于打印的代码
func walkPrint(nn *ir.CallExpr, init *ir.Nodes) ir.Node {
	// 在锁之前提升所有参数的计算。
	walkExprListCheap(nn.Args, init)

	// 对于println，在元素之间添加“”，并在末尾添加“\n”。
	if nn.Op() == ir.OPRINTN {
		s := nn.Args
		t := make([]ir.Node, 0, len(s)*2)
		for i, n := range s {
			if i != 0 {
				t = append(t, ir.NewString(" "))
			}
			t = append(t, n)
		}
		t = append(t, ir.NewString("\n"))
		nn.Args = t
	}

	// 折叠常量字符串的运行。
	s := nn.Args
	t := make([]ir.Node, 0, len(s))
	for i := 0; i < len(s); {
		var strs []string
		for i < len(s) && ir.IsConst(s[i], constant.String) {
			strs = append(strs, ir.StringVal(s[i]))
			i++
		}
		if len(strs) > 0 {
			t = append(t, ir.NewString(strings.Join(strs, "")))
		}
		if i < len(s) {
			t = append(t, s[i])
			i++
		}
	}
	nn.Args = t

	calls := []ir.Node{mkcall("printlock", nil, init)}
	for i, n := range nn.Args {
		if n.Op() == ir.OLITERAL {
			if n.Type() == types.UntypedRune {
				n = typecheck.DefaultLit(n, types.RuneType)
			}

			switch n.Val().Kind() {
			case constant.Int:
				n = typecheck.DefaultLit(n, types.Types[types.TINT64])

			case constant.Float:
				n = typecheck.DefaultLit(n, types.Types[types.TFLOAT64])
			}
		}

		if n.Op() != ir.OLITERAL && n.Type() != nil && n.Type().Kind() == types.TIDEAL {
			n = typecheck.DefaultLit(n, types.Types[types.TINT64])
		}
		n = typecheck.DefaultLit(n, nil)
		nn.Args[i] = n
		if n.Type() == nil || n.Type().Kind() == types.TFORW {
			continue
		}

		var on *ir.Name
		switch n.Type().Kind() {
		case types.TINTER:
			if n.Type().IsEmptyInterface() {
				on = typecheck.LookupRuntime("printeface")
			} else {
				on = typecheck.LookupRuntime("printiface")
			}
			on = typecheck.SubstArgTypes(on, n.Type()) // any-1 
		case types.TPTR:
			if n.Type().Elem().NotInHeap() {
				on = typecheck.LookupRuntime("printuintptr")
				n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
				n.SetType(types.Types[types.TUNSAFEPTR])
				n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
				n.SetType(types.Types[types.TUINTPTR])
				break
			}
			fallthrough
		case types.TCHAN, types.TMAP, types.TFUNC, types.TUNSAFEPTR:
			on = typecheck.LookupRuntime("printpointer")
			on = typecheck.SubstArgTypes(on, n.Type()) // any-1 
		case types.TSLICE:
			on = typecheck.LookupRuntime("printslice")
			on = typecheck.SubstArgTypes(on, n.Type()) // any-1 
		case types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64, types.TUINTPTR:
			if types.IsRuntimePkg(n.Type().Sym().Pkg) && n.Type().Sym().Name == "hex" {
				on = typecheck.LookupRuntime("printhex")
			} else {
				on = typecheck.LookupRuntime("printuint")
			}
		case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64:
			on = typecheck.LookupRuntime("printint")
		case types.TFLOAT32, types.TFLOAT64:
			on = typecheck.LookupRuntime("printfloat")
		case types.TCOMPLEX64, types.TCOMPLEX128:
			on = typecheck.LookupRuntime("printcomplex")
		case types.TBOOL:
			on = typecheck.LookupRuntime("printbool")
		case types.TSTRING:
			cs := ""
			if ir.IsConst(n, constant.String) {
				cs = ir.StringVal(n)
			}
			switch cs {
			case " ":
				on = typecheck.LookupRuntime("printsp")
			case "\n":
				on = typecheck.LookupRuntime("printnl")
			default:
				on = typecheck.LookupRuntime("printstring")
			}
		default:
			badtype(ir.OPRINT, n.Type(), nil)
			continue
		}

		r := ir.NewCallExpr(base.Pos, ir.OCALL, on, nil)
		if params := on.Type().Params().FieldSlice(); len(params) > 0 {
			t := params[0].Type
			n = typecheck.Conv(n, t)
			r.Args.Append(n)
		}
		calls = append(calls, r)
	}

	calls = append(calls, mkcall("printunlock", nil, init))

	typecheck.Stmts(calls)
	walkExprList(calls, init)

	r := ir.NewBlockStmt(base.Pos, nil)
	r.List = calls
	return walkStmt(typecheck.Stmt(r))
}

// walkRecover遍历ORECOVERFP节点。ducdefg
func walkRecoverFP(nn *ir.CallExpr, init *ir.Nodes) ir.Node {
	return mkcall("gorecover", nn.Type(), init, walkExpr(nn.Args[0], init))
}

func walkUnsafeSlice(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
	ptr := safeExpr(n.X, init)
	len := safeExpr(n.Y, init)

	fnname := "unsafeslice64"
	lenType := types.Types[types.TINT64]

	if ir.ShouldCheckPtr(ir.CurFunc, 1) {
		fnname = "unsafeslicecheckptr"
		// 为了简单起见，unsafeslicecheckptr始终使用int64 
	} else if len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size() {
		fnname = "unsafeslice"
		lenType = types.Types[types.TINT]
	}

	t := n.Type()

	// 调用运行时。unsafeslice{，64，checkptr}来检查ptr和len。
	fn := typecheck.LookupRuntime(fnname)
	init.Append(mkcall1(fn, nil, init, reflectdata.TypePtr(t.Elem()), typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]), typecheck.Conv(len, lenType)))

	h := ir.NewSliceHeaderExpr(n.Pos(), t,
		typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]),
		typecheck.Conv(len, types.Types[types.TINT]),
		typecheck.Conv(len, types.Types[types.TINT]))
	return walkExpr(typecheck.Expr(h), init)
}

func badtype(op ir.Op, tl, tr *types.Type) {
	var s string
	if tl != nil {
		s += fmt.Sprintf("\n\t%v", tl)
	}
	if tr != nil {
		s += fmt.Sprintf("\n\t%v", tr)
	}

	// 常见错误：*结构和*接口。
	if tl != nil && tr != nil && tl.IsPtr() && tr.IsPtr() {
		if tl.Elem().IsStruct() && tr.Elem().IsInterface() {
			s += "\n\t(*struct vs *interface)"
		} else if tl.Elem().IsInterface() && tr.Elem().IsStruct() {
			s += "\n\t(*interface vs *struct)"
		}
	}

	base.Errorf("illegal types for operand: %v%s", op, s)
}

func writebarrierfn(name string, l *types.Type, r *types.Type) ir.Node {
	fn := typecheck.LookupRuntime(name)
	fn = typecheck.SubstArgTypes(fn, l, r)
	return fn
}

// isRuneCount报告n的形式是否为len（[]符文（字符串））。
// 这些都优化为对运行时的调用。countrunes。
func isRuneCount(n ir.Node) bool {
	return base.Flag.N == 0 && !base.Flag.Cfg.Instrumenting && n.Op() == ir.OLEN && n.(*ir.UnaryExpr).X.Op() == ir.OSTR2RUNES
}
